package org.zhuzx.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 文本处理小工具，这里面的方法和GU中的字符串相关方法的区别：
 * 本类负责对单个主体做文本内容层面的处理，比如对它执行截取、替换、修饰等基于这个文本的细微操作。
 * 而GU中的方法不修改文本本身，主要是合并、判断等更粗粒度的操作。
 * @author	zhuzx	2016年3月4日
 */
public class MicroUtil {

	/**
	 * 需要转义的正则关键字列表。<br>
	 * 经实测，右花括号不需要转义，而右圆括号需要。
	 */
	private static final String[] NEED_ESCAPE_REGEX_META =
			{"\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "|"};
	private static final String[] NEED_ESCAPE_SQL = {"\\", "'", "\""};
	private static final String[] NEED_ESCAPE_SQL_LIKE = {"%", "_"};
	private static final int[] POWER_OF_TWO;
	static {
		POWER_OF_TWO = new int[31];//Integer.MAX_VALUE为2^31-1，得出int可表示的最大的幂数为30，最小为0。
		for (int i = 0; i < POWER_OF_TWO.length; i++) {
			POWER_OF_TWO[i] = 1 << i;
		}
	}
	
	/**
	 * 寻找字符序列中的匹配项，若无匹配结果则返回空数组。
	 * @param sequence 需要从中寻找匹配项的字符序列
	 * @param regex 匹配对象-正则表达式
	 * @return 由匹配项的索引号组成的数组
	 */
	public static int[] findMatches(CharSequence sequence, String regex) {
		Matcher mat = Pattern.compile(regex).matcher(sequence);
		List<Integer> found = new ArrayList<>();
		while (mat.find()) {
			found.add(mat.start());
		}
		int[] indexs = new int[found.size()];
		int i = 0;
		for (Integer index : found) {
			indexs[i] = index;
			i++;
		}
		return indexs;
	}
	
	/**
	 * 提取字符序列中的匹配项，若无匹配结果则返回空数组。
	 * @param sequence 需要从中寻找匹配项的字符序列
	 * @param regex 匹配对象-正则表达式
	 * @return 由匹配项组成的数组
	 */
	public static String[] extractMatches(CharSequence sequence, String regex) {
		Matcher mat = Pattern.compile(regex).matcher(sequence);
		List<String> matches = new ArrayList<>();
		while (mat.find()) {
			matches.add(mat.group());
		}
		return matches.toArray(new String[matches.size()]);
	}
	
	/**
	 * 寻找字符序列中的第一个匹配项
	 */
	public static int findFirst(CharSequence seq, String regex, int start) {
		CharSequence subSeq = seq.subSequence(start, seq.length());
		Matcher mat = Pattern.compile(regex).matcher(subSeq);
		if (mat.find()) {
			return start + mat.start();
		} else {
			return -1;
		}
	}
	
	/**
	 * 寻找字符序列中的第一个匹配项
	 */
	public static int findFirst(CharSequence seq, String regex) {
		return findFirst(seq, regex, 0);
	}
	
	/**
	 * 提取字符序列中的第一个匹配项
	 */
	public static String extractFirst(CharSequence seq, String regex) {
		Matcher mat = Pattern.compile(regex).matcher(seq);
		if (mat.find()) {
			return mat.group();
		} else {
			return null;
		}
	}
	
	/**
	 * 搜索第一个匹配字符的配对字符，忽略嵌套的匹配结果对。典型使用场景：从左括号左边的某个位置开始搜索其对应的右括号。
	 * @param seq 需要从中搜索配对的字符序列
	 * @param oneself 本身，如左括号。
	 * @param opposite 与oneself相对的字符，如右括号。
	 * @param start 在序列中搜索的起始位置
	 * @return 配对结果的起始位置，若无配对结果则返回-1。
	 */
	@Deprecated//临时的实现版本，需要改造成按char类型逐位匹配。
	public static int searchMate(CharSequence seq, char oneself, char opposite, int start) {
		return searchMateByRegex(seq, escapeSpecialChars(String.valueOf(oneself)),
				escapeSpecialChars(String.valueOf(opposite)), start);
	}
	
	/**
	 * 搜索第一个匹配字符的配对字符，详细说明参见内部调用的重载方法。
	 */
	public static int searchMate(CharSequence seq, char oneself, char opposite) {
		return searchMate(seq, oneself, opposite, 0);
	}
	
	@Deprecated//临时的实现版本，需要改造成按普通String匹配。
	public static int searchMate(CharSequence seq, String oneself, String opposite, int start) {
		return searchMateByRegex(seq, escapeSpecialChars(oneself), escapeSpecialChars(opposite), start);
	}
	
	/**
	 * 搜索第一个匹配字符的配对字符，详细说明参见内部调用的重载方法。
	 */
	public static int searchMate(CharSequence seq, String oneself, String opposite) {
		return searchMate(seq, oneself, opposite, 0);
	}
	
	/**
	 * 正则搜索第一个匹配字符串的配对项，忽略嵌套的匹配结果对。本方法是searchMate方法的增强版本。
	 * @param seq 需要从中搜索配对的字符序列
	 * @param oneself 本身（正则表达式）
	 * @param opposite 与oneself相对的字符串（正则表达式）
	 * @param start 在序列中搜索的起始位置
	 * @return 配对结果的起始位置，若无配对结果则返回-1。
	 */
	public static int searchMateByRegex(CharSequence seq, String oneself, String opposite, int start) {
		if (oneself.equals(opposite)) {
			CharSequence subSeq = (start > 0) ? seq.subSequence(start, seq.length()) : seq;
			int[] indexs = findMatches(subSeq, oneself);
			return (indexs.length >= 2) ? (indexs[1] + start) : -1;
		}
		CharSequence subSeq = seq.subSequence(start, seq.length());
		int[] indexs = findMatches(subSeq, "(" + oneself + ")|(" + opposite + ")");
		if (indexs.length < 2) return -1;
		String[] matches = extractMatches(subSeq, "(" + oneself + ")|(" + opposite + ")");
		int indicator = 0;
		int mate = 0;
		boolean startMatching = false;
		while (mate < matches.length) {
			if (matches[mate].matches(oneself)) {
				if (!startMatching) {
					startMatching = true;
				}
				indicator++;
			} else {
				if (!startMatching) continue;
				indicator--;
			}
			if (indicator == 0) {
				break;
			} else {
				mate++;
			}
		}
		return (indicator == 0) ? (start + indexs[mate]) : -1;
	}
	
	/**
	 * 搜索第一个匹配项的配对项，详细说明参见内部调用的重载方法。
	 */
	public static int searchMateByRegex(CharSequence seq, String oneself, String opposite) {
		return searchMateByRegex(seq, oneself, opposite, 0);
	}
	
	/**
	 * 根据指定的左边界和右边界搜索并截取第一对匹配项之间的字符串，忽略其中嵌套的边界。
	 */
	public static String substringByMates(CharSequence seq, char leftBoundary, char rightBoundary) {
		int start = seq.toString().indexOf(leftBoundary) + 1;
		if (start > 0) {
			int end = searchMate(seq, leftBoundary, rightBoundary);
			if (end >= start) {
				return seq.subSequence(start, end).toString();
			}
		}
		return null;
	}
	
	/**
	 * 根据指定的左边界和右边界搜索并截取第一对匹配项之间的字符串，忽略其中嵌套的边界。
	 */
	public static String substringByMates(CharSequence seq, String leftBoundary, String rightBoundary) {
		int start = seq.toString().indexOf(leftBoundary) + leftBoundary.length();
		if (start > 0) {
			int end = searchMate(seq, leftBoundary, rightBoundary);
			if (end >= start) {
				return seq.subSequence(start, end).toString();
			}
		}
		return null;
	}
	
	/**
	 * 根据指定的左边界和右边界（均为正则表达式）搜索并截取第一对匹配项之间的字符串，忽略其中嵌套的边界。
	 */
	public static String substringByMatesOnRegex(CharSequence seq,
			String leftBoundary, String rightBoundary) {
		Matcher mat = Pattern.compile(leftBoundary).matcher(seq);
		if (mat.find()) {
			int start = mat.end();//Returns the offset after the last character matched
			int end = searchMateByRegex(seq, leftBoundary, rightBoundary);
			if (end >= start) {//左边界与右边界重合（表达式相同或者匹配结果存在位置重叠）时返回null，连续时返回空字符串。
				return seq.subSequence(start, end).toString();
			}
		}
		return null;
	}
	
	/**
	 * 将下划线格式的字符串转为驼峰格式
	 */
	public static String convertUnderlineToCamelCased(String original) {
		StringBuilder camelCased = new StringBuilder(original.toLowerCase());
		int index = camelCased.indexOf("_");
		while (index > -1) {
			camelCased.replace(index, index+2, String.valueOf(Character.toUpperCase(camelCased.charAt(index+1))));
			index = camelCased.indexOf("_", index+1);
		}
		return camelCased.toString();
	}
	
	/**
	 * 将驼峰格式的字符串转为下划线格式
	 */
	public static String convertCamelCasedToUnderline(String original) {
		if (isUpperCase(original)) {//如果是全部大写的（非驼峰），则不能按后续逻辑给每个字母前都加下划线。
			return original;
		}
		StringBuilder sb = new StringBuilder(original);
		for (int i = 0; i < sb.length(); i++) {
			char c = sb.charAt(i);
			if (Character.isUpperCase(c) && i != 0) {//非首位的大写字母替换为_加小写字母
				sb.replace(i, i + 1, "_" + Character.toLowerCase(c));
				i++;//插入了一个下划线，再多往后挪一位。
			}
		}
		return sb.toString();
	}
	
	/**
	 * 将字符串中具有正则涵义的特殊字符全部转义
	 */
	public static String escapeSpecialChars(String str) {
		for (String key : NEED_ESCAPE_REGEX_META) {
			if (str.contains(key)) {
				str = str.replace(key, "\\" + key);
			}
		}
		return str;
	}
	
	/**
	 * 在limit的限制范围内反复循环式地将i值自增，如果i已经等于limit，则返回0。
	 */
	public static int increaseInCycle(int i, int limit) {
		return (i < limit) ? (i+1) : 0;
	}
	
	/**
	 * 创建重复的字符序列，返回长度为length的String，其中每个字符都是参数c。
	 */
	public static String createRepeatedCharSequence(char c, int length) {
		char[] chars = new char[length];
		for (int i = 0; i < length; i++) {
			chars[i] = c;
		}
		return new String(chars);
	}
	
	/**
	 * 将字符串的第一个字符转化为大写
	 */
	public static String capitalizeTheFirstChar(String str) {
		return str.substring(0, 1).toUpperCase() + str.substring(1);
	}
	
	/**
	 * 寻找大于等于number的最小的2的n次方值的对数值n。<br>
	 * 示例：输入1000、1024都返回10，输入16返回4，输入17返回5。
	 */
	public static int findMinLogarithmOfTwo(int number) {
		if (number < 1) throw new IllegalArgumentException("number必须为正整数");
		int index = Arrays.binarySearch(POWER_OF_TWO, number);
		if (index < 0) {
			index = -index - 1;
			if (index == POWER_OF_TWO.length) {
				index--;
			}
		}
		return index;
	}
	
	/**
	 * 寻找大于等于number的最小的2的n次方值。<br>
	 * 示例：输入1000、1024都返回1024，输入16返回16，输入17返回32。
	 */
	public static int findMinPowerOfTwo(int number) {
		return POWER_OF_TWO[findMinLogarithmOfTwo(number)];
	}
	
	/**
	 * 将文本中所有${参数名}和#{参数名}格式的占位符替换，得到填充后的新文本，填充值根据参数名从params中获取。<br>
	 * 其中：$表示直接替换，#表示额外在填充值前后加单引号。
	 */
	public static <K,V> String fillParameters(String text, Map<K,V> params) {
		for (Map.Entry<K,V> entry : params.entrySet()) {
			String key = GeneralUtil.toString(entry.getKey());
			String value = GeneralUtil.toString(entry.getValue());
			text = text.replace("${" + key + "}", value);
			text = text.replace("#{" + key + "}", "'" + value + "'");
		}
		return text;
	}
	
	/**
	 * 截取text中位于第一个label值前面的子字符串，若未找到label则返回本身。
	 */
	public static String subBefore(String text, char label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		int index = text.indexOf(label);
		return (index == -1) ? text : text.substring(0, index);
	}
	
	/**
	 * 截取text中位于第一个label值前面的子字符串，若未找到label则返回本身。
	 */
	public static String subBefore(String text, String label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		int index = text.indexOf(label);
		return (index == -1) ? text : text.substring(0, index);
	}
	
	/**
	 * 截取text中位于最后一个label值后面的子字符串，若未找到label则返回本身。
	 */
	public static String subBehind(String text, char label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		int index = text.lastIndexOf(label);
		return (index == -1) ? text : text.substring(index + 1);
	}
	
	/**
	 * 截取text中位于最后一个label值后面的子字符串，若未找到label则返回本身。
	 */
	public static String subBehind(String text, String label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		int index = text.lastIndexOf(label);
		return (index == -1) ? text : text.substring(index + label.length());
	}

	/**
	 * 截取text中位于第一个regexLabel值前面的子字符串，若未找到regexLabel则返回本身。
	 */
	public static String subBeforeByRegex(String text, String regexLabel) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		int index = findFirst(text, regexLabel);
		return (index == -1) ? text : text.substring(0, index);
	}

	/**
	 * 截取text中位于最后一个regexLabel值后面的子字符串，若未找到regexLabel则返回本身。
	 */
	public static String subBehindByRegex(String text, String regexLabel) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		String[] matches = extractMatches(text, regexLabel);
		return GeneralUtil.isEmpty(matches) ? text : subBehind(text, matches[matches.length - 1]);
	}

	/**
	 * 去掉text的前缀字符，若它不是以label开头则返回本身。
	 */
	public static String removePrefix(String text, String label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		return text.startsWith(label) ? text.substring(label.length()) : text;
	}

	/**
	 * 去掉text的后缀字符，若它不是以label结尾则返回本身。
	 */
	public static String removeSuffix(String text, String label) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		return text.endsWith(label) ? text.substring(0, text.length() - label.length()) : text;
	}

	/**
	 * 在text左右包装两个装饰符（仅当text有实际值时执行），并且不会产生重复的装饰符（即使text头尾已自带）。
	 */
	public static String wrapInMatesIfNotBlank(String leftDecor, String text, String rightDecor) {
		if (GeneralUtil.isBlank(text)) {
			return text;
		}
		if (!text.startsWith(leftDecor)) {
			text = leftDecor + text;
		}
		if (!text.endsWith(rightDecor)) {
			text = text + rightDecor;
		}
		return text;
	}
	
	/**
	 * 获取#0.00%百分比格式的字符串。<br>
	 * 示例：输入(52,100)返回52.00%，输入(1,3)返回33.33%，输入(5,4)返回125.00%。
	 * @param numerator 分子
	 * @param denominator 分母
	 */
	public static String getPercentage(int numerator, int denominator) {
		String rate = UnifiedConstants.getDef().format(
				((double)numerator) / denominator * 100);
		return rate + "%";
	}
	
	/**
	 * 从sql中提取第一个表名
	 */
	public static String extractFirstTableName(String sql) {
		String simpleSql = sql.trim().replaceAll("\\s+", " ").toLowerCase() + " ";
		return subBefore(subBehind(simpleSql, " from "), " ");
	}
	
	/**
	 * 是否全部为大写的字符串。不要求所有字符都是字母，但含有字母就必须是大写，含有任何小写字母就返回false。
	 */
	public static boolean isUpperCase(String str) {
		for (int i=0; i<str.length(); i++) {
			if (Character.isLowerCase(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 在正则匹配位置插入指定文本
	 */
	public static String insertTextAtFoundedPosition(String mainText, String regex, String insertion,
			boolean atBehind, boolean appendWhenNotFound) {
		int insertIndex = findFirst(mainText, regex);
		if (insertIndex > -1) {
			if (atBehind) {
				insertIndex += extractFirst(mainText, regex).length();
			}
		} else {
			if (appendWhenNotFound) {
				insertIndex = mainText.length();
			} else {
				return mainText;
			}
		}
		return mainText.substring(0, insertIndex) + insertion + mainText.substring(insertIndex);
	}
	
	/**
	 * 从全类名中截取不带类路径的简单类名，并移除内部类标识。
	 * @param className 全类名
	 */
	public static String getSimpleClassName(String className) {
		className = className.substring(className.lastIndexOf('.') + 1);//若没在任何包下也可以正常运行
		int index = className.indexOf('$');
		if (index > -1)
			className = className.substring(0, index);
		return className;
	}

	/**
	 * 将value中的特殊字符转义，使其能够作为参数值正确执行sql语句。<br>
	 * 注意：若value已经作了转义，则不能再调用本方法。
	 */
	public static String escapeSqlParamValue(String value) {
		if (GeneralUtil.isBlank(value)) {
			return value;
		}
		for (String word : NEED_ESCAPE_SQL) {
			value = value.replace(word, "\\" + word);
		}
		return value;
	}

	/**
	 * 将value中的特殊字符转义，使其能够作为参数值正确执行like查询。<br>
	 * 若非like查询，应优先考虑使用{@link #escapeSqlParamValue}方法。
	 */
	public static String escapeSqlParamLikeValue(String value) {
		if (GeneralUtil.isBlank(value)) {
			return value;
		}
		value = escapeSqlParamValue(value);
		for (String word : NEED_ESCAPE_SQL_LIKE) {
			value = value.replace(word, "\\" + word);
		}
		return value;
	}

	/** 迁移至{@link #escapeSqlParamLikeValue} */
	@Deprecated
	public static String escapeLikeValueForMysql(String value) {
		return escapeSqlParamLikeValue(value);
	}

	/**
	 * 分割字符串，无视分割符的正则涵义。结果数组中不含空字符串子项。
	 * @param str 目标字符串
	 * @param separator 纯文本分隔符，不支持正则表达式。
	 */
	public static String[] splitByStringAutoEscape(String str, String separator) {
		if (str == null) {
			return null;
		}
		if (str.startsWith(separator)) {
			str = str.substring(separator.length());
		}
		if (str.endsWith(separator)) {
			str = str.substring(0, str.length()-separator.length());
		}
		return str.split(MicroUtil.escapeSpecialChars(separator));
	}

	@Deprecated//初稿待整理
	@SuppressWarnings("unused")
	private static List<String> splitByStringHighPerformance(String str, String separator) {
		if (str == null) {
			return null;
		}
		char[] strChars = new char[str.length()];
		str.getChars(0, str.length(), strChars, 0);
		char[] sepChars = new char[separator.length()];
		separator.getChars(0, separator.length(), sepChars, 0);
		List<String> result = new ArrayList<>();
		int itemStart = 0;
		for (int i=0; i<strChars.length; i++) {
			int offset = 0;
			for (int j=0; j<sepChars.length && i<strChars.length; i++) {
				if (strChars[i] == sepChars[j]) {
					if (j == sepChars.length-1) {
						offset += sepChars.length;
						j = 0;
					} else {
						if (i == strChars.length-1) {
							i -= j;
							break;
						}
						j++;
					}
				} else {
					i -= j;
					break;
				}
			}
			if (offset > 0 || i == strChars.length) {
				int length = i - offset - itemStart;
				if (length > 0) {
					String item = new String(strChars, itemStart, length);
					result.add(item);
				}
				itemStart = i;
			}
		}
		if (itemStart < strChars.length) {
			String item = new String(strChars, itemStart, strChars.length-itemStart);
			result.add(item);
		}
		return result;
	}

	/**
	 * 按字符分割字符串，无视分割符的正则涵义。结果数组中不含空字符串子项。
	 */
	public static String[] splitByChar(String str, char separator) {
		if (str == null) {
			return null;
		}
		char[] chars = new char[str.length()];
		str.getChars(0, str.length(), chars, 0);
		List<String> result = new ArrayList<>();
		int offset = 0;
		for (int index=0; index<chars.length; index++) {
			if (chars[index] == separator) {
				int count = index - offset;
				if (count > 0) {
					result.add(new String(chars, offset, count));
				}
				offset = index + 1;
			}
		}
		if (offset < chars.length) {
			result.add(new String(chars, offset, chars.length-offset));
		}
		return result.toArray(new String[result.size()]);
	}

	/**
	 * 将以英文逗号连接的多个整型id值重新还原为id列表
	 */
	public static List<Integer> splitIds(String ids) {
		if (ids == null) {
			return null;
		}
		return Arrays.stream(splitByChar(ids, ',')).map(Integer::parseInt).collect(Collectors.toList());
	}

}